/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package no.ffi.fca;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author stian
 */
public abstract class Settings {
    /** Path to the openssl binary<br>default: /usr/bin/openssl */
    public static String BIN_OPENSSL;
    
    /** Path to the mv binary<br>default: /bin/mv */
    public static String BIN_MV;
    
    /** Path to the rm binary<br>default: /bin/rm */
    public static String BIN_RM;
    
    
    /** Full path to fCA/CA<br>default: /var/fCA/CA/ */
    public static final String PATH_CA;
    
    /** Full path to database file<br>default: /var/fCA/fCA.db */
    public static final String PATH_DATABASE;
    
    
    /** Full path to CA's certificate<br>default: /var/fCA/CA/cacert.pem */
    public static final String PATH_CA_CACERT;
    
    /** Full path to openssl conf directory<br>default: /var/fCA/CA/cnfs */
    public static final String PATH_CA_CNFS;
    
    /** 
     * Full path to openssl's config used when creating 
     * CA's certificate and CRL
     * <br>default: /var/fCA/CA/cnfs/caconfig.cnf
     */
    public static final String PATH_CA_CNFS_CACONFIG;
    
    /** 
     * Full path to a config file containing default values
     * which will be included in the temporary config file
     * used when creating new certificates with fCA
     * <br>default: /var/fCA/CA/cnfs/default.cnf */
    public static final String PATH_CA_CNFS_DEFAULT;
    
    /** Full path to the CRL<br>default: /var/fCA/CA/crl.pem */
    public static final String PATH_CA_CRL;
    
    /** 
     * Full path to directory holding CSR files
     * <br>default: /var/fCA/CA/csrs/ */
    public static final String PATH_CA_CSRS;
    
    /** Full path to openssl's index.txt<br>default: /var/fCA/CA/index.txt */
    public static final String PATH_CA_INDEX;
    
    /** 
     * Full path to directory holding PKCS#12 files
     * <br>default: /var/fCA/CA/pkcs12/ */
    public static final String PATH_CA_PKCS12;
    
    /** 
     * Full path to directory holding CA's private key
     * <br>default: /var/fCA/CA/private/ */
    public static final String PATH_CA_PRIVATE;

    /** 
     * Full path to CA's private key
     * <br>default: /var/fCA/CA/private/cakey.pem */
    public static final String PATH_CA_PRIVATE_CAKEY;
    
    /** Full path to openssl's serial file<br>default: /var/fCA/CA/serial */
    public static final String PATH_CA_SERIAL;
    
    /** 
     * Full path to directory holding signed certificates
     * <br>default: /var/fCA/CA/signed/ */
    public static final String PATH_CA_SIGNED;
    
    /** 
     * Full path to fCA's temporary directory for files generated
     * when creating new certificates
     * <br>default: /var/fCA/CA/tmp */
    public static final String PATH_CA_TMP;
    
    /** 
     * Full path to infineon's certificates which is
     * used to verify AIK requests
     * <br>default: /var/fCA/CA/tpm/infineon/ */
    public static final String PATH_CA_TPM_INFINEON;
    
    /** 
     * Full path to signed AIK certificates
     * <br>default: /var/fCA/CA/tpm/signed/ */
    public static final String PATH_CA_TPM_SIGNED;

    /** URL to fCAapi */
    public static String URL_fCAapi;
    
    /** URL to download CA's CRL */
    public static String URL_CRL;
    
    /** URL to download CA's certificate */
    public static String URL_CA;
    
    /** Path to fCA's root directory */
    public static final String PATH_ROOT;
    
    /** 
     * Compile-time-constant to a directory
     * where uploaded files are temporary stored */
    public static final String PATH_TMP = "/tmp";
    
    /** Path to fCA's config file */
    public static final String PATH_CONFIG;
    
    private static java.util.Properties props;
    private static boolean error;
    private static boolean allIsOkCached;

    static {
        if (System.getenv("FCA_ROOT") != null) {
            String envVar = System.getenv("FCA_ROOT");
            PATH_ROOT = envVar + (envVar.endsWith("/") ? "" : "/");
            System.out.println("Getting FCA_ROOT from environment variable: " + PATH_ROOT);
        }
        else {
            PATH_ROOT = "/var/fCA/";
            System.out.println("Choosing default FCA_ROOT: " + PATH_ROOT);
        }
        PATH_CONFIG = PATH_ROOT + "fCA.config";
        PATH_DATABASE = PATH_ROOT + "fCA.db";
        PATH_CA = PATH_ROOT + "CA/";

        PATH_CA_CACERT = PATH_CA + "cacert.pem";
        PATH_CA_CNFS = PATH_CA + "cnfs/";
        PATH_CA_CNFS_CACONFIG = PATH_CA_CNFS + "caconfig.cnf";
        PATH_CA_CNFS_DEFAULT = PATH_CA_CNFS + "default.cnf";
        PATH_CA_CRL = PATH_CA + "crl.pem";
        PATH_CA_CSRS = PATH_CA + "csrs/";
        PATH_CA_INDEX = PATH_CA + "index.txt";
        PATH_CA_PKCS12 = PATH_CA + "pkcs12/";
        PATH_CA_PRIVATE = PATH_CA + "private/";
        PATH_CA_PRIVATE_CAKEY = PATH_CA_PRIVATE + "cakey.pem";
        PATH_CA_SERIAL = PATH_CA + "serial";
        PATH_CA_SIGNED = PATH_CA + "signed/";
        PATH_CA_TMP = PATH_CA + "tmp/";
        PATH_CA_TPM_INFINEON = PATH_CA + "tpm/infineon/";
        PATH_CA_TPM_SIGNED = PATH_CA + "tpm/signed/";

        props = new Properties();
        allIsOkCached = false;
        loadValues();
    }

    /**
     * Check if CONFIG_FILE is read- and writable,
     * and if all settings can be loaded without errors
     */
    public static boolean isConfigOk() {
        loadValues();
        return !error;
    }

    /**
     * Check if all directories used by fCA exists and is writable
     */
    public static boolean isCaOk() {
        
        File filePathCa_CA                 = new File(Settings.PATH_CA);
        File filePathCa_CA_CACERT          = new File(Settings.PATH_CA_CACERT);
        File filePathCa_CA_CNFS            = new File(Settings.PATH_CA_CNFS);
        File filePathCa_CA_CNFS_CACONFIG   = new File(Settings.PATH_CA_CNFS_CACONFIG);
        File filePathCa_CA_CNFS_DEFAULT    = new File(Settings.PATH_CA_CNFS_DEFAULT);
        File filePathCa_CA_CSRS            = new File(Settings.PATH_CA_CSRS);
        File filePathCa_CA_INDEX           = new File(Settings.PATH_CA_INDEX);
        File filePathCa_CA_PKCS12          = new File(Settings.PATH_CA_PKCS12);
        File filePathCa_CA_PRIVATE         = new File(Settings.PATH_CA_PRIVATE);
        File filePathCa_CA_PRIVATE_CAKEY   = new File(Settings.PATH_CA_PRIVATE_CAKEY);
        File filePathCa_CA_SERIAL          = new File(Settings.PATH_CA_SERIAL);
        File filePathCa_CA_SIGNED          = new File(Settings.PATH_CA_SIGNED);
        File filePathCa_CA_TPM_INFINEON    = new File(Settings.PATH_CA_TPM_INFINEON);
        File filePathCa_CA_TPM_SIGNED      = new File(Settings.PATH_CA_TPM_SIGNED);

        //  CAN NOT WRITE                  OR          CAN NOT READ                              THEN NOT OK
        if (!filePathCa_CA.canWrite()               || !filePathCa_CA.canRead())                return false;
        if (                                           !filePathCa_CA_CACERT.canRead())         return false;
        if (!filePathCa_CA_CNFS.canWrite()          || !filePathCa_CA_CNFS.canRead())           return false;
        if (!filePathCa_CA_CNFS_CACONFIG.canWrite() || !filePathCa_CA_CNFS_CACONFIG.canRead())  return false;
        if (!filePathCa_CA_CNFS_DEFAULT.canWrite()  || !filePathCa_CA_CNFS_DEFAULT.canRead())   return false;
        if (!filePathCa_CA_CSRS.canWrite()          || !filePathCa_CA_CSRS.canRead())           return false;
        if (!filePathCa_CA_INDEX.canWrite()         || !filePathCa_CA_INDEX.canRead())          return false;
        if (!filePathCa_CA_PKCS12.canWrite()        || !filePathCa_CA_PKCS12.canRead())         return false;
        if (!filePathCa_CA_PRIVATE.canWrite()       || !filePathCa_CA_PRIVATE.canRead())        return false;
        if (                                           !filePathCa_CA_PRIVATE_CAKEY.canRead())  return false;
        if (!filePathCa_CA_SERIAL.canWrite()        || !filePathCa_CA_SERIAL.canRead())         return false;
        if (!filePathCa_CA_SIGNED.canWrite()        || !filePathCa_CA_SIGNED.canRead())         return false;
        if (                                           !filePathCa_CA_TPM_INFINEON.canRead())   return false;
        if (!filePathCa_CA_TPM_SIGNED.canWrite()    || !filePathCa_CA_TPM_SIGNED.canRead())     return false;
        return true;
    }
    
    /**
     * Tests isConfigOk() and isCaOk()<br>
     * <br>
     * Return true if previously run of isAllOk returned true 
     * and useCache is true, of if isConfigOk() and isCaOk() returns true<br>
     * <br>
     * Return false if isConfigOk() or isCaOk() return false
     */
    public static boolean isAllOk(boolean useCache) {
        if (useCache && allIsOkCached) return true;
        boolean ret;
        if (isConfigOk() && isCaOk()) ret = true;
        else ret = false;
        allIsOkCached = ret;
        return allIsOkCached;
    }

    public static void loadValues() {
        error = false;
        try {
            FileInputStream f = new FileInputStream(PATH_CONFIG);
            props.load(f);
            System.out.println("Properties loaded from " + PATH_CONFIG + ":");
            props.list(System.out);
        } catch (Exception e) { 
            System.out.println("Unable to load property file (" + PATH_CONFIG + ")");
            e.printStackTrace(); 
            error = true;
            return;
        }
        BIN_OPENSSL = getProperty("BIN_OPENSSL");
        BIN_RM = getProperty("BIN_RM");
        BIN_MV = getProperty("BIN_MV");
        
        URL_fCAapi = getProperty("URL_fCAapi");
        URL_CRL = getProperty("URL_CRL");
        URL_CA = getProperty("URL_CA");
        if (error) {
            System.out.println("Settings error:");
            System.out.println("There was an error when loading values from " + PATH_CONFIG);
        }
    }
    
    public static void printValues() {
        for (Object o : props.keySet()) {
            String s = (String) o;
            System.out.println(s + " = " + getProperty(s));
        }
    }

    public static boolean setProperty(String name,String value){
        props.setProperty(name, value);
        System.out.println("The property "+name+" has been updated with value "+ value);
       
        try {
            FileOutputStream file = new FileOutputStream(PATH_CONFIG); 
            props.store(file,"Updated "+name);
            /*FileInputStream f = new FileInputStream(fileName);
            props.load(f);
            System.out.println("Properties loaded after update");
            props.list(System.out);
            f.close();*/
            file.close();
        } catch (IOException ex) {
            Logger.getLogger(no.ffi.tpm.Config.class.getName()).log(Level.SEVERE, null, ex);
            return false;
        } 
        return true;
    }
    
    private static String getProperty(String name) {
        return getProperty(name,null);
    }
    
    private static String getProperty(String name, String def) {
        String s = props.getProperty(name, def);
        if (s == null) { 
            System.out.println("Null retur for " + name + " property");
            error = true;
            return null;
        }
        else return s.trim();
    }

}
